/****************************************************************************************
 * File:			Architecture.cs
 * Description:		Implementation of the common functionality for a customisable interface
 *					between environment and agent (in either direction). The architecture 
 *                  provides storage and management of hierarchical collections of components.
 ***************************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace XGAE.Framework
{
    /// <summary>
    /// Container class that stores components transparently. It provides the common 
    /// functionality required to initialise, update and maintain components.
    /// </summary>
    public class Architecture : Component
    {     
        #region Fields
                                           
        private Dictionary<string, Node> m_nodes; // storage of components registered in architecture
        private Queue<Schedule> m_schedule; // components that need auto-updating
        private int m_time; // keep track of time for scheduling
        private string m_architecture; // filename of XML architecture definition 
        private string m_knowledge; // XML filename containing the knowledge

        #endregion

        #region Structures

        /// <summary>
        /// Helper structure to store components and imported interfaces. The architecture is 
        /// managed as a set of connected nodes. This data structure references the component 
        /// for each node and associates the imported interfaces with another component name.
        /// </summary>
	    private struct Node
	    {
            Component component; // reference to component for this node

            public Component Component
            {
                get { return this.component; }
                set { this.component = value; }
            }

            Dictionary<string, Link> connection; // mapping from interface import name to component

            public Dictionary<string, Link> Connection
            {
                get { return this.connection; }
                set { this.connection = value; }
            }

		    /*/// <summary>
		    /// Constructor initialises the reference.
		    /// </summary>
		    Node() : base(null) {}*/

            /// <summary>
            /// Assignment operator that initialises the Node from a Component.
            /// </summary>
            public void SetAssignment(Component component)
            {
                this.component = component;
                this.connection.Clear();
            }

		    /// <summary>
		    /// An interface link from one component to another.  The export member is used as a 
		    /// parameter to the source component (type of the interface), and the import member 
		    /// corresponds to the target component (name of the interface).
		    /// </summary>
		    internal struct Link
		    {
                string source; // name of component providing interface

                public string Source
                {
                    get { return this.source; }
                    set { this.source = value; }
                }

			    string type; // type of interface exported from source

                public string Type
                {
                    get { return this.type; }
                    set { this.type = value; }
                }

			    /*/// <summary>
			    /// Default constructor for the STL
			    /// </summary>
                internal Link() { }*/

			    /// <summary>
			    /// convenience constructor
			    /// </summary>
			    /// <param name="source"></param>
			    /// <param name="type"></param>
                internal Link(string source, string type)
			    {
                    this.source = source;
                    this.type = type;
			    }
		    }
	    }
	
        /// <summary>
        /// Keep track of 
        /// </summary>
	    private struct Schedule
	    {
            private int period; // number of frames before update

            internal int Period
            {
                get { return this.period; }
            }

            private Services.IExecute execute; // interface used to update component

            internal Services.IExecute Execute
            {
                get { return this.execute; }
            }

            /*/// <summary>
            /// Default constructor.
            /// </summary>
            internal Schedule()
            {
                this.period = 0;
                this.execute = null;
            }*/

		    /// <summary>
            /// User constructor for convenience.
		    /// </summary>
		    /// <param name="period"></param>
		    /// <param name="execute"></param>
		    internal Schedule(int period, Services.IExecute execute)
		    {
                this.period = period;
                this.execute = execute;
		    }
	    }

        #endregion

        #region C'tors

        /// <summary>
        /// Constructor sets default values and initialises the architecture.
        /// </summary>
        /// <param name="type"></param>
        public Architecture(string type) : base(type)
        {
            m_architecture = "Architecture.xml";
            m_knowledge = "Knowledge.xml";
            m_time = 0;
        }

        /*/// <summary>
        /// Empty virtual destructor.
        /// </summary>
	    public virtual ~Architecture()
	    {
        }*/

        #endregion

        #region Private Methods

        /// <summary>
        /// Scan the architecture file. Create components when they don't exist, and store the 
        /// interface connections for the assembly phase later.
        /// </summary>
        /// <param name="config">The XML node of the architecture definition.</param>
        /// <param name="component">Name of the component currently being loaded.
        /// </param>
        private void RecursiveLoad(Extensions.XmlDataTree config, string component)
        {
            // main loop checks for valid XML tags
            foreach (Extensions.XmlDataTree xdt in config.Branches)
	        {
                // determine the intended name of this node
                string name, type;
                name = xdt.GetSecondLeaveByKey("name");
                type = xdt.GetSecondLeaveByKey("type");

                if (name == string.Empty)
                    name = type;

                // components nodes specify which modules need loading
                if (xdt.Name == "component")
                {
                    // allocate and store a new component if necessary
                    if (!m_nodes.ContainsKey(name))
                        m_nodes[name].SetAssignment(this.Retrieve(type));
                    // traverse to the scope tag containing other components
                    this.RecursiveLoad(xdt, name);
                }
                else
                // components may import arbitrary interfaces
                    if (xdt.Name == "import" || xdt.Name == "scope")
                {
                    RecursiveLoad(xdt, component);
                } 
                else
	            // components may import arbitrary interfaces
		        if (xdt.Name == "interface")
		        {
		            // gather the detalis about the connection
			        string source_component = xdt.GetSecondLeaveByKey("source");
		            // add a connection from component to source_component
			        m_nodes[component].Connection[name] = new Node.Link(source_component, type);
		        }
                // the components can have update requests
		        if (xdt.Name == "update")
		        {	
                    // determine the update period specified
                    int p = (int)(Convert.ToSingle(xdt.GetSecondLeaveByKey("period")) * 10.0f);
                    // fetch the execute interface that will be used for the update
                    Services.IExecute i = (Services.IExecute)(m_nodes[component].Component.Export("Execute"));
                    // there's a problem if the cast returns null
                    if (i == null)
                    {
                        //Warning("Automatic polling for component " + component + " failed; no valid interface found.");
                    }
                    // otherwise, just add it to the back of the queue
                    else 
                        m_schedule.Enqueue(new Schedule((p > 0 ? p : 1), i));
		        }
            }
        }

        /// <summary>
        /// Build a nested architecture of components.  The architecture description is used to 
        /// assemble the components, which are initialized with the knowledge. The loading is done
        /// in two phases.  First, we recurse over the definition of the architecture, creating
        /// new modules as we go along.  The second phase loads all the data into each of these modules.
        /// </summary>
        private void Construct()
        {
            string name;
            // locate the components in the architecture and determine how they are connected
            Extensions.XmlDataTree arch = new Extensions.XmlDataTree(m_architecture);
            // determine the intended name for this component
            name = arch.GetSecondLeaveByKey("name");

            if (name == string.Empty)
                name = m_type;

            // store this component in the registry as the root of the architecture
            m_nodes[name].SetAssignment(this);
            // traverse the architecture definition to find components and interface imports
            this.RecursiveLoad(arch, name);

            // now scan each of the nodes in the architecture and connect them up
            foreach (KeyValuePair<string, Node> n in m_nodes)
            {
                Node node = n.Value;
                // for this node, traverse each of the connections to other components
                foreach(KeyValuePair<string, Node.Link> l in node.Connection)
                {
                    Node.Link link = l.Value;
                    // check that the source component actually exists
                    if(!m_nodes.ContainsKey(link.Source))
                    {
                        //Warning("Source component " + link.source + " not found.");
                    }
                    else
                    {
                        // export the interface from the source component
                        Interface i = m_nodes[link.Source].Component.Export(link.Type);
                        // pass it to the target component if valid
                        if (i!=null)
					        node.Component.Import(l.Key, i);
                    }
                }
            }

            // if it's present, load the knowledge XML file from the same directory
            try
            {
                Extensions.XmlDataTree kb = new Extensions.XmlDataTree(m_knowledge);
	            // each element of this document is a chunk of knowledge for a component
                foreach (Extensions.XmlDataTree xdt in kb.Branches)
                {
                    // determine the name of this element
                    string _name = xdt.Name;
                    // see if it exists in the architecture we've loaded
                    // it's not there, so skip straight to the next one
                    if (!m_nodes.ContainsKey(_name))
                        continue;
                    // if it exists, call the virtual load function to internalize the data
			        Node n = m_nodes[_name];
                    // protect the framework's integrity from the user's untrusted code
			        try
			        {
			            // this function is usually generated automatically 
		 		        n.Component.Load(xdt);
			        }
			        catch (Exception ex)
			        {
				        //Warning(node_iter->first + "::Load() raised an exception.");
			        }
                }
            }
            // an error with the knowledge file is fine, it's not important
            catch (Exception ex)
            {
                // cerr << "Persistent information not loaded from file" << m_knowledge  << "." << endl;
            }
        }

        /// <summary>
        /// Scan all the components recursively and use selective persistence to write the XML to 
        /// file.  Then free the memory.
        /// </summary>
        private void Destroy()
        {
            // now tell the architecture to initialize all components
            foreach(KeyValuePair<string,Node> kvp in m_nodes)
            {
                // make a virtual call to the overloaded function coded by the user
                Node node = kvp.Value;
                // free this component from memory, whichever scope
                if(node.Component != this && node.Component.Type != "Backend")
                    this.Remove(node.Component);
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Architecture loading overrides component loading to load nested components.
        /// Call the automatically generated load procedure for this component, and then recursively
        /// build the architecture of components by calling construct.
        /// </summary>
        /// <param name="config"></param>
        /// <param name="parent"></param>
        public override void PreLoad(Extensions.XmlDataTree config, Architecture parent)
        {
            // store the parent for later
            m_parent = parent;

            // call the meta-generated procedure to load XML
            this.Load(config);

            // determine the true location of the files based on the directory
            m_architecture = config.GetSecondLeaveByKey("directory") + m_architecture;
            m_knowledge = config.GetSecondLeaveByKey("directory") + m_knowledge;

            // build up the hierarchy of components
            this.Construct();

            // now tell the architecture to initialize all components            
            foreach(KeyValuePair<string, Node> kvp in m_nodes)
            {
                // make a virtual call to the overloaded function coded by the user
                Node node = kvp.Value;
                // don't initialise self until all children have been processed first
                if (node.Component == (Component)this)
                    continue;
                // wrap the user's code in layers of protective padding!
                try
                {
                    // this function is called to prepare data once interfaces are connected
                    node.Component.Init();
                }
                catch (Exception ex)
                {
                    //Warning(n->first + "::Init() raised an exception.");
                }
            }

            // only now initialise this component, as dependencies are fulfulled
            this.Init();
        }

        /// <summary>
        /// Call the automatically generated save procedure for this component, and then recursively
        /// teardown the architecture of components by calling construct.
        /// Overloaded function that prepares the saving.
        /// </summary>
        /// <param name="config"></param>
        public override void PreSave(Extensions.XmlDataTree config)
        {
            // now tell the architecture to initialize all components
            foreach(KeyValuePair<string, Node> kvp in m_nodes)
            {
                // make a virtual call to the overloaded function coded by the user
                Node node = kvp.Value;
                // wrap the user's code in layers of protective padding!
                try
                {
                    // prepare the data for writing
                    node.Component.Done();
                }
                catch (Exception ex)
                {
                    //Warning(n->first + "::Done() raised an exception.");
                }
            }

            // call the meta-generated procedure to save XML
            this.Save(config);
            // tear down the hierarchy of components
            this.Destroy();
        }

        /// <summary>
        /// Create a new component by scanning the local scope first, then calling the parent
        /// architecture if necessary. Check the factory if it can create the component.
        /// If not, ask the parent architecture.
        /// </summary>
        /// <param name="type">Name of the architecture.</param>
        /// <returns></returns>
        public Component Retrieve(string type)
        {
            // the most reliable entity for creating a component is the local factory
            try
            {
                return Factory.Create(type);
            }
            // if the component is not found, then it may be within another scope
            catch (Exception ex)
            {
                // in this case, ask the parent to instanciate a component
                if (m_parent!=null)
                {
                    Component c = m_parent.Retrieve(type);
                    return c;
                }
                // there's definitely a problem here, so throw the error again
                else
                {
                    //throw Exception("Cannot retrieve component " + type, __FILE__, __LINE__);
                    return null;
                }
            }
        }

        /// <summary>
        /// Add a component to the scope if possible.  This will generally be done by 
        /// architectures only, so that they can pass the Body and other global components around.
        /// </summary>
        /// <param name="name">String representing the name of the component.</param>
        /// <param name="component">Referense to the component to be added.</param>
	    public override void Include(string name, Component component)
	    {
		    m_nodes[name].SetAssignment(component);
	    }

        /// <summary>
        /// Delete the component in the appropriate place, either in this scope or in the scope
        /// of the parent architecture. Check the factory if it can create the component.
        /// If not, ask the parent architecture.
        /// </summary>
        /// <param name="component">Referense to the component to be destroyed.</param>
        public void Remove(Component component)
        {
            // the most reliable entity for deleting component is the local factory
            try
            {
                Factory.Delete(component);
            }
            // if the component is not found, then it may be within another scope
            catch (Exception ex)
            {
                // in this case, ask the parent to instanciate a component
                if (m_parent!=null)
                {
                    m_parent.Remove(component);
                }
                // there's definitely a problem here, so throw the error again
                else
                {
                    //throw Exception("Cannot remove component " + c->GetType(), __FILE__, __LINE__);
                }
            }
        } 

        /// <summary>
        /// Automatically process the components that require updates on a regulal basis.
        /// Determine if any of the components need updating at this frame.
        /// </summary>
        public void Update()
        {
            // scan through each of the components that need an automatic update
            foreach(Schedule s in m_schedule)
            {
                // the components only get called as often as requested
                if (m_time % s.Period == 0)
                    // this is done via the standard Execute interface
                    s.Execute.Tick();	
            }

            // increment the polling timer
            ++m_time;	
        }

        /// <summary>
        /// Implemented by components that need to emmit messages.
        /// </summary>
        /// <param name="message">A downcast message that must be sent.</param>
	    public override void Broadcast(Message message)
	    {
            base.Broadcast(message);
            foreach (KeyValuePair<string, Node> kvp in m_nodes)
            {
                kvp.Value.Component.Dispatch(message);
		    }
        }

        /*
        /// <summary>
        /// Pass a message on to the parent so it can sent to all the other components in the scope.
        /// </summary>
        /// <param name="message"></param>
        void Component::Broadcast(Message message)
        {
	        if (m_parent)
		        m_parent.Broadcast(message);

        }
        */

        #endregion
    }
}  
